#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include "epollserver.h"

Clnode *client_head;

int init(int port)
{
	int sockfd;
	struct sockaddr_in addr;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);

	int opt = SO_REUSEADDR;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
	{
		close(sockfd);
		perror("bind error:");
		return -1;
	}
	listen(sockfd, 10);
	return sockfd;
}

int server()
{
	int sockfd, efd, res, nready, connfd, csockfd, i, client_num;
	unsigned int clilen;
	struct epoll_event tep, ep[OPEN_MAX];
	struct sockaddr_in cliaddr;
	char str[20];

	sockfd = init(34000);
	efd = epoll_create(OPEN_MAX);
	if (efd == -1)
	{
		perror("epoll_create");
		return 0;
	}

	tep.events = EPOLLIN;
	tep.data.fd = sockfd;
	res = epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &tep);
	client_num = 0;
	if (res == -1)
	{
		perror("epoll_ctl");
		return 0;
	}

	while(1)
	{
		nready = epoll_wait(efd, ep, OPEN_MAX, -1);
	//	printf("nready:%d\n", nready);
		if(nready == -1)
		{
			perror("nready");
			return 0;
		}

		for(i = 0 ; i < nready ; i++)
		{
			if(!(ep[i].events & EPOLLIN))
				continue;
			if(client_num >= 10000)
			{
				printf("too many client\n");
				continue;
			}
			if(ep[i].data.fd == sockfd)
			{
				clilen = sizeof(cliaddr);
				connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
				printf("connected:%s[%d]\n", 
				inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), 
				ntohs(cliaddr.sin_port));

				tep.events = EPOLLIN;
				tep.data.fd = connfd;
				res = epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &tep);
				add_client(connfd, cliaddr);
				client_num++;
				if (res == -1)
				{
					perror("epoll_ctl");
					return 0;
				}

			}
			else
			{	
				csockfd = ep[i].data.fd;
				
				if(recvice(csockfd))
				{
					res = epoll_ctl(efd, EPOLL_CTL_DEL, csockfd, NULL);
					if (res == -1)
					{
						perror("epoll_ctl");
						return 0;
					}
					close(csockfd);
				}
				
			}
		}
		
	}

	return 0;
}

int recvice(int sockfd)
{
	int len;
	char buf[1024], sendbuf[1024];

	len = read(sockfd, buf, sizeof(buf));
	if(len == 0)
		return 0;
	else
	{
		*(buf+len) = '\0';
		if(!strcmp(buf, "quit"))
		{
			printf("disconnect[%d]\n", sockfd);
			del_client(sockfd);
			list_client();
			return 1;
		}
		else if(!strncmp(buf, "setname ", 8))
		{
			if(setname(sockfd ,buf + 8))
			{
				sprintf(sendbuf,"rename:%s", buf + 8);
				len += 8;
				write(sockfd, sendbuf, len);
				broad_send(sendbuf, sockfd ,len);
			}
		} 
		else if(!strcmp(buf, "listfile"))
		{
			list_file(sockfd);
		}
		else if(!strncmp(buf, "download ", 9))
		{
			send_file(sockfd, buf + 9);
		} 
		else if(!strncmp(buf, "upload ", 7))
		{
			recv_file(sockfd);
		}
		else
		{
			broad_send(buf, sockfd, len);
		}
	}

	return 0;
}

void recv_file(int sockfd)
{
	int len, fd, i;
	char recv[1024], filename[100], *p;

	read(sockfd, recv, sizeof(recv));
	p = strchr(recv, '\0');
	p++;
	strcpy(filename, p);

	fd = open(filename, O_WRONLY | O_CREAT, 0666);
	if(fd == -1)
		printf("creat file error\n");

	while((len = read(sockfd, recv, sizeof(recv))) > 0)
	{
		write(fd, recv, len);
	}
}

void send_file(int sockfd, char *buf)
{
	int fd, flen ,len = 0;
	char filepath[300], sendbuf[1024], filename[1024];

	sprintf(filepath,"%s%s", "/home/ysh/Desktop/epoll/server/bin/lib/", buf);
	fd = open(filepath, O_RDONLY);
	if(fd == -1)
		printf("open file error\n");

	len += sprintf(filename, "filename%c%s", 0, buf);

	write(sockfd, filename, sizeof(filename));
	while((flen = read(fd, sendbuf, sizeof(sendbuf))) > 0)
	{
		write(sockfd, sendbuf, flen);
	}
}

void broad_send(char *data, int sockfd, int len)
{
	Clnode *p, *act;
	char sendbuf[1024];

	for(act = client_head; act -> sockfd != sockfd ; act = act -> next);
	for(p = client_head; p != NULL ; p = p -> next)
	{
		if(p -> sockfd == sockfd)
		{	

		}
		else
		{
			printf("sendto[%d]:%s\n", p->sockfd, p -> ip);
			len += sprintf(sendbuf, "%s[%s]:%s", act -> name, act -> ip, data);
			write(p -> sockfd, sendbuf, len);
		}
	}
}



void del_client(int csockfd)
{
	Clnode *p, *q;
	
	if(client_head -> sockfd == csockfd)
	{
		p = client_head -> next;
		free(client_head);
		client_head = p;
	}
	else
	{
		p = client_head -> next;
		q = client_head;
		while(p -> next != NULL)
		{
			if(p -> sockfd == csockfd)
				break;
			p = p -> next;
			q = q -> next;
		}
		if(p -> next == NULL)
		{
			q -> next = NULL;
			free(p);
		}
		else
		{
			q -> next = p -> next;
			free(p);
		}
	}
	

}

void add_client(int csockfd, struct sockaddr_in cliaddr)
{
	Clnode *p, *node, *q;
	char name[20], str[20];
	int i;

	inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str));
	if(client_head == NULL)
	{
		client_head = (Clnode *)malloc(sizeof(Clnode));
		sprintf(name, "Guest%d", 1);
		strcpy(client_head -> name , name);
		strcpy(client_head -> ip, str);
		client_head -> sockfd =csockfd;
		client_head -> next = NULL;
	}
	else if(client_head -> next == NULL)
	{
		node = (Clnode *)malloc(sizeof(Clnode));
		sprintf(name, "Guest%d", 2);
		strcpy(node -> name ,name);
		strcpy(node -> ip, str);
		node -> sockfd =csockfd;
		client_head -> next = node;
		node -> next = NULL;
	}
	else
	{
		p = client_head -> next;
		q = client_head;
		i = 2;
		while(p != NULL)
		{
			p = p -> next;
			q = q -> next;
			i++;
		}

		p = (Clnode *)malloc(sizeof(Clnode));
		sprintf(name, "Guest%d", i);
		strcpy(p -> name , name);
		strcpy(p -> ip, str);
		p -> sockfd = csockfd;
		q -> next = p;
		p -> next = NULL;
	}
	
}

void list_client()
{
	Clnode *p;
	p = client_head;
	printf("=================================\n");
	while(p != NULL)
	{
		printf("[%d]name:%s\tip:%s\n", p -> sockfd
			, p -> name, p -> ip);
		p = p -> next;
	}
	printf("=================================\n");
}

int setname(int sockfd ,char *rename)
{
	Clnode *p;

	p = client_head;

	while(p != NULL)
	{
		if(p -> sockfd == sockfd)
			break;
		p = p -> next;
	}

	if(p != NULL)
	{
		strcpy(p -> name, rename);
		return 1;
	}
	return 0;
}

void list_file(int sockfd)
{
	DIR *dirp;
	char filepath[300];
	struct dirent *envdir;
	struct stat buf;

	char sendbuf[1024];
	int len = 0;

	dirp = opendir("/home/ysh/Desktop/epoll/server/bin/lib");
	if(dirp == NULL)
	{
		printf("opendir error\n");
	}
	len += sprintf(sendbuf, "%s%c", "libr", 0);
	while((envdir = readdir(dirp)) != NULL)
	{
		memset(filepath, 0, sizeof(filepath));
		sprintf(filepath, "/home/ysh/Desktop/epoll/server/bin/lib/%s", envdir -> d_name);

		stat(filepath, &buf);
		if(S_ISDIR(buf.st_mode))
		{
			continue;
		}
		len += sprintf(sendbuf+len, "%s%c", envdir -> d_name, 0);
	}
	len += sprintf(sendbuf+len, "%c", 0);

	write(sockfd, sendbuf, len);
	closedir(dirp);
}
